// Copyright 2012 Google Inc. All Rights Reserved.

package com.google.superlists.servlet;

import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.client.http.GenericUrl;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.Drive.Files.Get;
import com.google.api.services.drive.model.File;
import com.google.api.services.tasks.Tasks;
import com.google.api.services.tasks.Tasks.Tasklists;
import com.google.api.services.tasks.model.TaskList;
import com.google.api.services.tasks.model.TaskLists;
import com.google.common.base.Charsets;
import com.google.common.util.concurrent.Futures;
import com.google.superlists.model.ActionResult;
import com.google.superlists.model.SuperList;
import com.google.superlists.model.SuperListContent;
import com.google.superlists.model.SuperTaskList;
import com.google.superlists.model.SuperTaskLists;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.Future;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Action that loads and displays the main editor page.
 *
 * @author sven@google.com (Sven Mawson)
 */
public class LoadAction extends BaseAction {

  private final String id;

  public LoadAction(
      HttpServlet servlet, HttpServletRequest req, HttpServletResponse resp, String id) {
    super(servlet, req, resp);
    this.id = id;
  }

  @Override
  protected ActionResult handle() throws IOException {
    SuperList list = new SuperList();
    list.id = id;

    Tasks tasksService = getTasksService();
    Drive driveService = getDriveService();

    Tasklists.List listRequest = tasksService.tasklists().list();
    if (usePartialResponses()) {
      listRequest.setFields("items(id,title)");
    }

    Get getRequest = null;
    if ("".equals(id)) {
      list.name = "New List";
    } else {
      getRequest = driveService.files().get(id);
      if (usePartialResponses()) {
        getRequest.setFields("title,downloadUrl,userPermission/role");
      }
    }

    Future<File> fileFuture = null;
    Future<TaskLists> taskListsFuture;
    if (useBatch()) {
      BatchRequest batch = driveService.batch();

      if (getRequest != null) {
        FutureCallback<File> getCallback = createCallback("load.files.getLength");
        getRequest.queue(batch, getCallback);
        fileFuture = getCallback.getFuture();
      }

      FutureCallback<TaskLists> callback = createCallback("load.tasklists.listLength");
      listRequest.queue(batch, callback);
      taskListsFuture = callback.getFuture();

      int batchSize = batch.size();
      start("load.batch(" + batchSize + ")");
      batch.execute();
      stop("load.batch(" + batchSize + ")");
    } else {

      if (getRequest != null) {
        start("load.files.get()");
        File file = getRequest.execute();
        fileFuture = Futures.immediateFuture(file);
        addContentLength("load.files.getLength", file.getResponseHeaders());
        addContentLength("response.size", file.getResponseHeaders());
        stop("load.files.get()");
      }

      start("load.tasklists.list()");
      TaskLists tasklists = listRequest.execute();
      taskListsFuture = Futures.immediateFuture(tasklists);
      addContentLength("load.tasklists.listLength", tasklists.getResponseHeaders());
      addContentLength("response.size", tasklists.getResponseHeaders());
      stop("load.tasklists.list()");
    }

    if (fileFuture != null) {
      handleFile(getRequest, list, Futures.getUnchecked(fileFuture));
    }
    SuperTaskLists taskLists = convertTaskLists(Futures.getUnchecked(taskListsFuture));

    // Set the attributes.
    req.setAttribute("list", list);
    req.setAttribute("tasklists", taskLists);

    // No action result, we dispatch to a JSP instead.
    return null;
  }

  /**
   * Handles a drive file by downloading its contents and setting fields on the
   * list.
   */
  private void handleFile(Get getRequest, SuperList list, File file) throws IOException {
    // Set the name based on the title of the file.
    list.name = file.getTitle();

    // If we have file contents, parse it into the list content.
    start("load.media.download()");
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    getRequest.getMediaHttpDownloader().download(new GenericUrl(file.getDownloadUrl()), out);
    String content = new String(out.toByteArray(), Charsets.UTF_8);
    if (content != null) {
      list.content = SuperListContent.fromJson(content);
      addMetric("load.mediaLength", content.length());
      addMetric("response.size", content.length());
    }
    stop("load.media.download()");
  }

  /**
   * Converts a {@link TaskLists} object into a {@link SuperTaskLists} object.
   */
  private SuperTaskLists convertTaskLists(TaskLists taskLists) {
    SuperTaskLists localLists = new SuperTaskLists();
    for (TaskList taskList : taskLists.getItems()) {
      SuperTaskList localList = new SuperTaskList();
      localList.id = taskList.getId();
      localList.title = taskList.getTitle();
      localLists.lists.add(localList);
    }
    return localLists;
  }
}
